package com.steellee.leeexcel.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.data.DataFormatData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.steellee.leeexcel.dto.MetaCellStyle;
import com.steellee.leeexcel.dto.MetaDataOperate;
import com.steellee.leeexcel.dto.MetaExcelWriteDTO;
import com.steellee.leeexcel.exception.BusinessException;
import com.steellee.leeexcel.exception.MsgCode;
import com.steellee.leeexcel.msg.JSONMessage;
import com.steellee.leeexcel.util.CellStyleUtil;
import com.steellee.leeexcel.util.CustomCellWriteHandler;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;

import java.io.File;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel文件写出方法实现类
 * <p>
 * +  * @author steellee
 *
 * @date 2023-02-17
 */
public class PaExcelWriteStart {

    private static final Logger logger = LoggerFactory.getLogger(PaExcelWriteStart.class);
    private static final Integer operateOne = 0;
    private static final Integer operateTwo = 1;
    private static final String SUCCESS_MSG = "文件写出成功! ";

    /**
     * 单个 sheet数据写出方案
     * +
     *
     * @param clazz        数据对象类.class
     * @param dataList     写出数据集合
     * @param sheetName    sheet名称
     * @param outputStream 文件流
     * @param filePath     文件全路径
     * @return
     */
    public JSONMessage write(Class clazz, List<Object> dataList, String sheetName, OutputStream outputStream, String filePath) {
        long start = System.currentTimeMillis();
        try {
            Integer operateType = getOperateType(outputStream, filePath);
            List<MetaExcelWriteDTO> metas = new ArrayList<>();
            MetaExcelWriteDTO meta = new MetaExcelWriteDTO().setParamClazz(clazz).setDataList(dataList).setSheetName(sheetName);
            metas.add(meta);
            writeFile(operateType, outputStream, filePath, metas);
        } catch (BusinessException e) {
            return JSONMessage.error(e.getCode(), e.getMessage());
        }
        logger.info("writefileusetime:{}ms", System.currentTimeMillis() - start);
        return JSONMessage.success(MsgCode.FILE_ANALYSIS_SUCCESS.code, SUCCESS_MSG);
    }

    /**
     * 多个 sheet数据写出方案
     * ★
     *
     * @param param        入参参数集合
     * @param outputStream 文件流
     * @param filePath     文件全路径
     * @return
     */
    public JSONMessage write(List<MetaExcelWriteDTO> param, OutputStream outputStream, String filePath, Boolean isMap) {
        long start = System.currentTimeMillis();
        if (null == isMap) {
            JSONMessage.error(MsgCode.CHECK_IS_MAP.code, MsgCode.CHECK_IS_MAP.msg);
        }
        try {
            Integer operateType = getOperateType(outputStream, filePath);
            if (isMap) {
                writeMapFile(operateType, outputStream, filePath, param);
            } else {
                writeFile(operateType, outputStream, filePath, param);
            }
        } catch (BusinessException e) {
            return JSONMessage.error(e.getCode(), e.getMessage());
        }
        logger.info("writefileusetime:{}ms", System.currentTimeMillis() - start);
        return JSONMessage.success(MsgCode.FILE_ANALYSIS_SUCCESS.code, SUCCESS_MSG);
    }

    /**
     * 单个 sheet数据写出方案-对象入参
     *
     * @param param        入参参数集合
     * @param outputStream 文件流
     * @param filePath     文件全路径
     * @ return
     **/
    public JSONMessage write(MetaExcelWriteDTO param, OutputStream outputStream, String filePath, Boolean isMap) {
        long start = System.currentTimeMillis();
        if (null == isMap) {
            JSONMessage.error(MsgCode.CHECK_IS_MAP.code, MsgCode.CHECK_IS_MAP.msg);
        }
        List<MetaExcelWriteDTO> paramDto = new ArrayList<>();
        paramDto.add(param);
        try {
            Integer operateType = getOperateType(outputStream, filePath);
            if (isMap) {
                writeMapFile(operateType, outputStream, filePath, paramDto);
            } else {
                writeFile(operateType, outputStream, filePath, paramDto);
            }
        } catch (BusinessException e) {
            return JSONMessage.error(e.getCode(), e.getMessage());
        }
        logger.info("writefileusetime:{}ms", System.currentTimeMillis() - start);
        return JSONMessage.success(MsgCode.FILE_ANALYSIS_SUCCESS.code, SUCCESS_MSG);
    }

    /**
     * 获取入参方案 1入参文件流， 2入参文件全路径
     * %
     *
     * @param outputStream
     * @param filePath
     * @return
     */
    private Integer getOperateType(OutputStream outputStream, String filePath) {
        if (null != outputStream && StringUtils.isEmpty(filePath)) {
            return operateOne;
        } else if (null == outputStream && !StringUtils.isEmpty(filePath)) {
            return operateTwo;
        } else {
            throw new BusinessException(MsgCode.WRITE_OPERATE_ERR.code, MsgCode.WRITE_OPERATE_ERR.msg);
        }
    }

    /**
     * 将MAP数据写入指定路径文件或文件流
     *
     * @param operateType  1入参文件流, 2入参文件全路径
     * @param outputstream 文件流
     * @param filePath     文件全路径
     * @param param        入参对象集合
     */
    private void writeMapFile(Integer operateType, OutputStream outputstream, String filePath, List<MetaExcelWriteDTO> param) {
        ExcelWriterBuilder excelWriterBuilder = setBuilder(operateType, outputstream, filePath);
        try {
            ExcelWriter excelWriter = excelWriterBuilder.build();
            Integer num = 0;
            for (MetaExcelWriteDTO dto : param) {
                CustomCellWriteHandler customCellWriteHandler = new CustomCellWriteHandler();
                customCellWriteHandler.setColumnWidth(dto.getPaColumnWidth());
                if (null == dto.getSheetNo()) {
                    dto.setSheetNo(num);
                }
                num++;
                if (StringUtils.isEmpty(dto.getSheetName())) {
                    dto.setSheetName("sheet "+ num);
                }
                WriteSheet writeSheet = EasyExcel.writerSheet(dto.getSheetNo(), dto.getSheetName()).head(dto.getHeads())
                        .registerWriteHandler(new CellWriteHandler() {
                            @Override
                            public void afterCellDispose(CellWriteHandlerContext context) {
                                if (!dto.isHasOwnStyle()) {
                                    CellStyleUtil.setWriteCellStyle(context, dto.getHeadRows(), true);
                                } else {
                                    WriteCellData cellData = context.getFirstCellData();
                                    Cell cell = context.getCell();
                                    if (null != dto.getHeadCellStyle() && !ObjectUtils.isEmpty(dto.getHeadRows())) {
                                        if (dto.getHeadRows().contains(context.getCell().getRowIndex())) {
                                            dto.getHeadCellStyle().setWrapped(true);
                                            dto.getHeadCellStyle().setLocked(true);
                                            cellData.setWriteCellStyle(dto.getHeadCellStyle());
                                        }
                                    }

                                    if (null != dto.getDataOperateList()) {
                                        if (!dto.getHeadRows().contains(context.getCell().getRowIndex())) {
                                            if (null != dto.getGlobalStyle()) {
                                                cellData.setWriteCellStyle(dto.getGlobalStyle());
                                            }
                                            for (MetaDataOperate operate : dto.getDataOperateList()) {
                                                List<Integer> columnList = CellStyleUtil.getColumnList(operate);
                                                List<Integer> rowList = CellStyleUtil.getMapRowList(dto.getHeadRows().size(), dto.getMapList(), operate);
                                                if (rowList.contains(cell.getRowIndex()) && columnList.contains(cell.getColumnIndex())) {
                                                    if (null != operate.getMetaCellStyle()) {
                                                        cellData.setWriteCellStyle(operate.getMetaCellStyle());
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    //设置单元格纯文本格式
                                    DataFormatData data = new DataFormatData();
                                    data.setIndex((short) 49);
                                    WriteCellStyle writeCellStyle = context.getFirstCellData().getWriteCellStyle();
                                    writeCellStyle.setDataFormatData(data);
                                }
                            }
                        }).registerWriteHandler(customCellWriteHandler).build();
                excelWriter.write(dto.getMapList(), writeSheet);
            }
            excelWriter.finish();
        } catch (Exception e) {
            throw new BusinessException(MsgCode.WRITE_STREAM_ERR.code, MsgCode.WRITE_STREAM_ERR.msg);
        }
    }

    /**
     * 将 obj数据写入指定路径文件或文件流
     *
     * @param operateType  1入参文件流， 2入参文件全路径
     * @param outputStream 文件流
     * @param filePath     文件全路径
     * @param param        入参对象集合
     */
    private void writeFile(Integer operateType, OutputStream outputStream, String
            filePath, List<MetaExcelWriteDTO> param) {
        ExcelWriterBuilder excelWriterBuilder = setBuilder(operateType, outputStream, filePath);
        DataFormatData data = new DataFormatData();
        data.setIndex((short) 49);
        try {
            ExcelWriter excelWriter = excelWriterBuilder.build();
            int num = 0;
            for (MetaExcelWriteDTO dto : param) {
                CustomCellWriteHandler customCellWriteHandler = new CustomCellWriteHandler();
                customCellWriteHandler.setColumnWidth(dto.getPaColumnWidth());
                if (null == dto.getSheetNo()) {
                    dto.setSheetNo(num);
                }
                num++;
                if (StringUtils.isEmpty(dto.getSheetName())) {
                    dto.setSheetName("sheet "+ num);
                }
                WriteSheet writeSheet = EasyExcel.writerSheet(dto.getSheetNo(), dto.getSheetName()).head(dto.getParamClazz())
                        .registerWriteHandler(new CellWriteHandler() {
                            @Override
                            public void afterCellDispose(CellWriteHandlerContext context) {
                                if (!dto.isHasOwnStyle()) {
                                    CellStyleUtil.setWriteCellStyle(context, dto.getHeadRows(), false);
                                } else {
                                    WriteCellData cellData = context.getFirstCellData();
                                    Cell cell = context.getCell();
                                    if (null != dto.getHeadCellStyle() && !ObjectUtils.isEmpty(dto.getHeadRows())) {
                                        if (dto.getHeadRows().contains(cell.getRowIndex())) {
                                            dto.getHeadCellStyle().setWrapped(true);
                                            dto.getHeadCellStyle().setLocked(true);
                                            CellStyleUtil.copyValue(cellData.getWriteCellStyle(), dto.getHeadCellStyle());
                                        }

                                        if (!ObjectUtils.isEmpty(dto.getDataOperateList())) {
                                            if (ObjectUtils.isEmpty(dto.getHeadRows()) || !dto.getHeadRows().contains(cell.getRowIndex())) {
                                                if (null != dto.getGlobalStyle()) {
                                                    CellStyleUtil.copyValue(cellData.getWriteCellStyle(), dto.getGlobalStyle());
                                                }

                                                // 反射取DataCellStyle值 并赋值给WriteCellStyle,目的保留DATA数据对象注解
                                                for (MetaDataOperate operate : dto.getDataOperateList()) {
                                                    List<Integer> columnList = CellStyleUtil.getColumnList(operate);
                                                    List<Integer> rowList = CellStyleUtil.getObjRowList(dto.getHeadRows().size(), dto.getDataList(), operate);
                                                    if (rowList.contains(cell.getRowIndex()) && columnList.contains(cell.getColumnIndex())) {
                                                        if (null != operate.getMetaCellStyle()) {
                                                            CellStyleUtil.copyValue(cellData.getWriteCellStyle(), operate.getMetaCellStyle());
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }

                                //补充单元格赋值文本格式
                                WriteCellStyle style = context.getFirstCellData().getWriteCellStyle();
                                style.setDataFormatData(data);
                            }
                        }).registerWriteHandler(customCellWriteHandler).build();
                excelWriter.write(dto.getDataList(), writeSheet);
            }
            excelWriter.finish();
        } catch (Exception e) {
            throw new BusinessException(MsgCode.WRITE_STREAM_ERR.code, MsgCode.WRITE_STREAM_ERR.msg);
        }
    }

    private void setCellStyle(WriteCellData cellData, Short colorIndex) {
        WriteCellStyle writeCellStyle = new WriteCellStyle();
        writeCellStyle.setFillForegroundColor(colorIndex);
        writeCellStyle.setBorderBottom(BorderStyle.THIN);
        writeCellStyle.setBorderLeft(BorderStyle.THIN);
        writeCellStyle.setBorderRight(BorderStyle.THIN);
        writeCellStyle.setBorderTop(BorderStyle.THIN);
        writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        cellData.setWriteCellStyle(writeCellStyle);
    }

    /**
     * 判断数据返回方式
     *
     * @param operateType
     * @param outputStream
     * @param filePath
     **/
    private ExcelWriterBuilder setBuilder(Integer operateType, OutputStream outputStream, String filePath) {
        ExcelWriterBuilder excelWriterBuilder = null;
        if (operateOne == operateType) {
            excelWriterBuilder = EasyExcel.write(outputStream);
        }
        if (operateTwo == operateType) {
            checkFilePath(filePath);
            try {
                excelWriterBuilder = EasyExcel.write(filePath);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return excelWriterBuilder;
    }

    /**
     * 文件路径校验是否存在
     * ★
     *
     * @param filePath 文件全路径
     */
    private void checkFilePath(String filePath) {
        String filePathUrl = filePath.substring(0, filePath.lastIndexOf("\\"));
        File file = new File(filePathUrl);
        if (!file.exists()) {
            throw new BusinessException(MsgCode.FILE_PATH_ERR.code, MsgCode.FILE_PATH_ERR.msg);
        }
    }
}